package l2ft.gameserver.idfactory;

import gnu.trove.list.array.TIntArrayList;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Arrays;

import l2ft.gameserver.database.DatabaseFactory;
import l2ft.gameserver.database.MySql;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public abstract class IdFactory
{
	private static final Logger _log = LoggerFactory.getLogger(IdFactory.class);

	public static final String[][] EXTRACT_OBJ_ID_TABLES = {
		{ "characters", "obj_id" },
		{ "items", "object_id" },
		{ "clan_data", "clan_id" },
		{ "ally_data", "ally_id" },
		{ "pets", "objId" },
		{ "couples", "id" } };

	public static final int FIRST_OID = 0x10000000;
	public static final int LAST_OID = 0x7FFFFFFF;
	public static final int FREE_OBJECT_ID_SIZE = LAST_OID - FIRST_OID;

	protected static final IdFactory _instance = new BitSetIDFactory();

	public static final IdFactory getInstance()
	{
		return _instance;
	}

	protected boolean initialized;
	protected long releasedCount = 0;

	protected IdFactory()
	{
		resetOnlineStatus();
	}

	private void resetOnlineStatus()
	{
		MySql.simple_update("UPDATE characters SET online = 0");
		_log.info("IdFactory: Clear characters online status.");
	}

	protected int[] extractUsedObjectIDTable() throws SQLException
	{
		TIntArrayList objectIds = new TIntArrayList();

		try(Connection con = DatabaseFactory.getConnection())
		{
			for(String[] table : EXTRACT_OBJ_ID_TABLES)
			{
				try(PreparedStatement statement = con.prepareStatement("SELECT " + table[1] + " FROM " + table[0]);
						ResultSet rs = statement.executeQuery())
				{
					int size = objectIds.size();
					while(rs.next())
						objectIds.add(rs.getInt(1));
	
					size = objectIds.size() - size;
					if(size > 0)
						_log.info("IdFactory: Extracted " + size + " used id's from " + table[0]);
				}
			}
		}

		int[] extracted = objectIds.toArray();

		Arrays.sort(extracted);

		_log.info("IdFactory: Extracted total " + extracted.length + " used id's.");

		return extracted;
	}

	public boolean isInitialized()
	{
		return initialized;
	}

	public abstract int getNextId();

	/**
	 * return a used Object ID back to the pool
	 * @param object ID
	 */
	public void releaseId(int id)
	{
		releasedCount++;
	}

	public long getReleasedCount()
	{
		return releasedCount;
	}

	public abstract int size();
}